# !/usr/bin/env python3
# -*- coding: utf-8 -*-

import math
import xml.etree.ElementTree as EleTree
from UI.demo_2.ui_struct_bak import TSNNode
import os
from util.topo_adapter import get_topo_set, get_time_slot_len
from util.flow_adapter import get_route_path
from util.lib import get_node_type


def get_node_port_type_set(topo_xml, node_type_set):
    # 根据topo_xml，将link_id替换成节点id和端口号
    root = EleTree.parse(topo_xml)
    link_ele_set = root.findall('entry')
    link_node_port_set = []
    for link_ele in link_ele_set:
        link_id = link_ele.findtext('link_id')
        node_id = link_ele.findtext('src_node')
        port_id = link_ele.findtext('src_port')
        dst_node_id = link_ele.findtext('dst_node')
        for node_type_ele in node_type_set:
            if node_type_ele['node_id'] == int(node_id):
                node_type = node_type_ele['node_type']
                link_node_port_set.append({'link_id': link_id,
                                           'src_node_id': node_id,
                                           'src_port_id': port_id,
                                           'dst_node_id': dst_node_id,
                                           'node_type': node_type})
    return link_node_port_set


def get_link_node_port_set(topo_xml):
    # 根据topo_xml，将link_id替换成节点id和端口号
    root = EleTree.parse(topo_xml)
    link_ele_set = root.findall('entry')
    link_node_port_set = []
    for link_ele in link_ele_set:
        link_id = link_ele.findtext('link_id')
        node_id = link_ele.findtext('src_node')
        port_id = link_ele.findtext('src_port')
        dst_node_id = link_ele.findtext('dst_node')
        link_node_port_set.append({'link_id': link_id,
                                   'src_node_id': node_id,
                                   'src_port_id': port_id,
                                   'dst_node_id': dst_node_id
                                   })
    return link_node_port_set


def init_topo(topo_xml, para_xml):
    node_type_set = get_node_type(para_xml)
    link_node_port_set = get_node_port_type_set(topo_xml, node_type_set)

    # 将link_id_set整理成属于同一node的格式
    node_id_set = []
    for item in link_node_port_set:
        if item['src_node_id'] not in node_id_set:
            node_id_set.append(item['src_node_id'])

    node_set = []
    for item in node_id_set:
        node_set.append({'node_id': item,
                         'node_type': None,
                         })

    for item in link_node_port_set:
        for node in node_set:
            if node['node_id'] == item['src_node_id'] and \
                    node['node_type'] is None:
                node_type = None
                if item['node_type'] == 1:
                    node_type = 'NIC'
                elif item['node_type'] == 0:
                    node_type = 'SW'
                node['node_type'] = node_type
    return node_set


def init_tsn_node(node_set,
                  topo_xml):
    tsn_node_set = []
    for item in node_set:
        node = TSNNode(**item)
        tsn_node_set.append(node)

    # 开始添加孩子节点
    link_node_port_set = get_link_node_port_set(topo_xml)
    for item in link_node_port_set:
        src_node_id = item['src_node_id']
        dst_node_id = item['dst_node_id']
        parent_node = None
        child_node = None
        for node in tsn_node_set:
            if node.node_id == src_node_id:
                parent_node = node
            if node.node_id == dst_node_id:
                child_node = node
        parent_node.appendChildren(child_node)

    # [print(node.children) for node in tsn_node_set]
    return tsn_node_set


def get_tsn_node_set(topo_xml):
    node_set = init_topo(topo_xml)
    tsn_node_set = init_tsn_node(node_set,
                                 topo_xml)
    return tsn_node_set


# 从plan.xml里面获取所有的网络节点
def parse_plan_xml(plan_xml,
                   topo_xml):
    # 首先获取所有的链路id
    root = EleTree.parse(plan_xml)
    link_ele_set = root.findall('link')
    link_id_set = []
    for link_ele in link_ele_set:
        link_id = link_ele.findtext('link_id')
        link_id_set.append(link_id)
    # print(link_id_set)
    # 根据topo_xml，将link_id替换成节点id和端口号
    root = EleTree.parse(topo_xml)
    link_ele_set = root.findall('entry')
    link_node_port_id_set = []
    for link_ele in link_ele_set:
        link_id = link_ele.findtext('link_id')
        node_id = link_ele.findtext('src_node_id')
        port_id = link_ele.findtext('src_port_id')
        link_node_port_id_set.append((link_id, node_id, port_id))
    # print(link_node_port_id_set)

    # 将link_id_set整理成属于同一node的格式
    node_id_set = []
    for item in link_node_port_id_set:
        if item[1] not in node_id_set:
            node_id_set.append(item[1])
    node_port_set = []
    for item in node_id_set:
        node_port_set.append({'node_id': item,
                              'port_set': []})

    for item in link_node_port_id_set:
        for node in node_port_set:
            if node['node_id'] == item[1]:
                node['port_set'].append(item[2])

    for node in node_port_set:
        port_set = node['port_set']
        port_set = [int(i) for i in port_set]
        port_set.sort()
        node['port_set'] = [str(i) for i in port_set]

    # print(node_id_set)
    # print(node_port_set)
    node_id_set = [int(i) for i in node_id_set]
    node_id_set.sort()
    node_id_set = [str(i) for i in node_id_set]
    return node_id_set, node_port_set


def get_slot_and_hyper_period(flow_xml,
                              topo_xml):
    # 获取时间槽长度
    macrotick = get_time_slot_len(topo_xml)

    # 获取流量的周期
    root = EleTree.parse(flow_xml)
    period_ele_set = root.iter('period')
    period_set = [int(period_ele.text) for period_ele in period_ele_set]
    # print(period_set)
    # 计算调度周期
    hyper_period = 1
    for period in period_set:
        hyper_period = int(period) * int(hyper_period) / \
                       math.gcd(int(period), int(hyper_period))

    return macrotick, hyper_period


def get_plan(plan_xml,
             topo_xml):
    # 从plan_xml中获取每条链路每个时间槽内发送报文的情况，
    # 形如[{'link_id': link_id, 'sched_set':
    # [{'slot_id': slot_id, 'flow_id': flow_id, 'st_queue': queue_id}, ...]}]
    # 从topo_xml中获取链路与源节点和源端口的对应关系
    # 将plan_set中的链路替换成节点和端口
    #
    root = EleTree.parse(plan_xml)
    link_ele_set = root.findall('link')
    plan_set = []
    for link_ele in link_ele_set:
        link_id = link_ele.findtext('link_id')
        flow_set = []
        slot_set = link_ele.iter('macrotick_id')
        flow_id_set = link_ele.iter('flow_id')
        for slot, flow_id in zip(slot_set, flow_id_set):
            flow_set.append({slot.tag: slot.text,
                             flow_id.tag: flow_id.text})
        plan_set.append({'link_id': link_id,
                         'flow_set': flow_set})

    link_node_port_set = get_link_node_port_set(topo_xml)
    for link in plan_set:
        link_id = link['link_id']
        for item in link_node_port_set:
            if item['link_id'] == link_id:
                link.update({'src_node_id': item['src_node_id'],
                             'dst_node_id': item['dst_node_id']})

    return plan_set


def get_stream(flow_xml,
               topo_xml):
    # 根据topo_xml，获取topo_set
    topo_set = get_topo_set(topo_xml)
    # 获取流的路由路径
    route_set = get_route_path(flow_xml, topo_set)

    root = EleTree.parse(flow_xml)
    flow_ele_set = root.iter('stream_id')
    flow_ele_set = [item.text for item in flow_ele_set]

    route_set_res = []
    for flow_ele in flow_ele_set:
        for route_ele in route_set:
            if int(flow_ele) == route_ele['flow_id']:
                src_node_id_set = [str(data[0]) for data in route_ele['route_node_port'][0:-1]]
                dst_node_id_set = [str(data[0]) for data in route_ele['route_node_port'][1:]]
                path = []
                for i in range(len(route_ele['route_node_port'])-1):
                    path.append((src_node_id_set[i], dst_node_id_set[i]))
                route_set_res.append(path)

    return flow_ele_set, route_set_res


# 获取注入时刻表
def get_inject_tbl(inject_tbl_xml):
    inject_tbl = []
    root = EleTree.parse(inject_tbl_xml)
    node_ele_set = root.findall('node')
    for node_ele in node_ele_set:
        node_id = node_ele.findtext('node_id')
        entry_set = []
        entry_ele_set = node_ele.findall('entry')
        for entry_ele in entry_ele_set:
            # valid = entry_ele.findtext('valid')
            # reserved = entry_ele.findtext('reserved')
            dbuf_id = entry_ele.findtext('dbuf_id')
            time_slot = entry_ele.findtext('time_slot')
            entry_set.append({
                # 'valid': valid,
                # 'reserved': reserved,
                'dbuf_id': dbuf_id,
                'time_slot': time_slot
            })
        inject_tbl.append({'node_id': node_id,
                           'entry_set': entry_set})
    return inject_tbl


def get_map_tbl(map_tbl_xml):
    map_tbl = []
    root = EleTree.parse(map_tbl_xml)
    node_ele_set = root.findall('node')
    for node_ele in node_ele_set:
        node_id = node_ele.findtext('node_id')
        entry_set = []
        entry_ele_set = node_ele.findall('entry')
        for entry_ele in entry_ele_set:
            # valid = entry_ele.find('valid')
            # reserved = entry_ele.find('reserved')
            flow_id = entry_ele.find('flow_id')
            five_tuple_mask = entry_ele.find('five_tuple_mask')
            ip_protocol = entry_ele.find('ip_protocol')
            dst_port = entry_ele.find('dst_port')
            src_port = entry_ele.find('src_port')
            dst_ip = entry_ele.find('dst_ip')
            src_ip = entry_ele.find('src_ip')
            entry_set.append({
                # valid.tag: valid.text,
                # reserved.tag: reserved.text,
                flow_id.tag: flow_id.text,
                five_tuple_mask.tag: five_tuple_mask.text,
                ip_protocol.tag: ip_protocol.text,
                dst_port.tag: dst_port.text,
                src_port.tag: src_port.text,
                dst_ip.tag: dst_ip.text,
                src_ip.tag: src_ip.text
            })
        map_tbl.append({
            'node_id': node_id,
            'entry_set': entry_set
        })
    # print(map_tbl)
    return map_tbl


def get_tbl(tbl_xml):
    tbl = []

    # xml文本不存在时，直接退出
    if os.access(tbl_xml, os.F_OK) == False:
        return tbl

    root = EleTree.parse(tbl_xml)
    node_ele_set = root.findall('node')
    for node_ele in node_ele_set:
        node_id = node_ele.findtext('node_id')
        entry_ele_set = node_ele.findall('entry')
        entry_set = []
        for entry_ele in entry_ele_set:
            flow_id = entry_ele.findtext('flow_id')
            outport = entry_ele.findtext('outport')
            priority = entry_ele.findtext('priority')
            flow_period = entry_ele.findtext('flow_period')
            inject_time_slot = entry_ele.findtext('inject_time_slot')
            dic = {'flow_id': flow_id,
                   'outport': outport,
                   'priority': priority,
                   'flow_period': flow_period,
                   'inject_time_slot': inject_time_slot}
            entry_set.append(dic)
        tbl.append({'node_id': node_id,
                    'entry_set': entry_set})

    return tbl


def get_gcl_tbl(gcl_tbl_xml):
    tbl = []
    root = EleTree.parse(gcl_tbl_xml)
    node_ele_set = root.findall('node')
    for node_ele in node_ele_set:
        node_id = node_ele.findtext('node_id')
        port_ele_set = node_ele.findall('port')
        sched_set = []
        for port_ele in port_ele_set:
            port_id = port_ele.findtext('port_id')
            entry_ele_set = port_ele.findall('entry')
            for entry_ele in entry_ele_set:
                slot_ele = entry_ele.findtext('slot')
                gate_ele = entry_ele.findtext('gate')
                dic = {'port_id': port_id,
                       'slot': slot_ele,
                       'gate': gate_ele}
                sched_set.append(dic)
        tbl.append({'node_id': node_id,
                    'entry_set': sched_set})

    return tbl


def get_para_tbl(para_tbl_xml):
    tbl = []
    root = EleTree.parse(para_tbl_xml)
    node_ele_set = root.findall('node')
    for node_ele in node_ele_set:
        para_ele_set = list(node_ele)
        dic = {}
        for item in para_ele_set:
            dic.update({item.tag: item.text})
        tbl.append(dic)

    return tbl


def get_aux_tbl(test_aux_tbl_xml):
    test_aux_tbl = []
    root = EleTree.parse(test_aux_tbl_xml)
    node_ele_set = root.findall('node')
    for node_ele in node_ele_set:
        node_id = node_ele.findtext('node_id')
        dic = {}
        dic.update({'node_id': node_id})
        entry_ele_set = node_ele.findall('entry')
        for entry_ele in entry_ele_set:
            for index in range(0, len(entry_ele)):
                dic.update({entry_ele[index].tag: entry_ele[index].text})
        test_aux_tbl.append(dic)

    return test_aux_tbl


def get_cb_tbl(cb_tbl_xml):
    cb_tbl = []
    root = EleTree.parse(cb_tbl_xml)
    node_ele_set = root.findall('node')
    for node_ele in node_ele_set:
        node_id = node_ele.findtext('node_id')
        entry_set = []
        entry_ele_set = node_ele.findall('entry')
        for entry_ele in entry_ele_set:
            flow_id = entry_ele.findtext('flow_id')
            ci = entry_ele.findtext('ci')
            dic = {'flow_id': flow_id, 'ci': ci}
            entry_set.append(dic)
        cb_tbl.append({'node_id': node_id,
                       'entry_set': entry_set})

    return cb_tbl


def main():
    output_tbl_path = '../output/tbl/'
    cb_tbl_xml = output_tbl_path + 'cb_tbl.xml'
    cb_tbl = get_cb_tbl(cb_tbl_xml)
    [print(entry) for entry in cb_tbl]


if __name__ == '__main__':
    main()
